`timescale 1ns / 1ps

`include "data_width.vh"

module edge_info_pre #(parameter
    DST_ID_DWIDTH       = `DST_ID_DWIDTH,
    EDGE_OFF_DWIDTH     = `EDGE_OFF_DWIDTH,
    VERTEX_PIPE_NUM     = `VERTEX_PIPE_NUM,
    MEM_AWIDTH          = `MEM_AWIDTH,
    VERTEX_MASK_WIDTH   = `VERTEX_MASK_WIDTH
    ) (
        input                                                   clk,
        input                                                   front_rst,
        input [EDGE_OFF_DWIDTH * (VERTEX_PIPE_NUM + 1) - 1 : 0] front_edge_off,
        input [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]         front_dst_id,
        input [VERTEX_PIPE_NUM - 1 : 0]                         front_dst_data_valid,
        input                                                   back_stage_vertex_full,

        // para in
        input [MEM_AWIDTH - 1 : 0]                              front_edge_info_addr_ed,
        input [EDGE_OFF_DWIDTH - 1 : 0]                         front_edge_off_ed,
        input [EDGE_OFF_DWIDTH - VERTEX_MASK_WIDTH - 1 : 0]     front_mem_edge_ed,
        input                                                   front_para_valid,

        // para out
        output                                                  rst,
        output [MEM_AWIDTH - 1 : 0]                             edge_info_addr_ed,
        output [EDGE_OFF_DWIDTH - 1 : 0]                        edge_off_ed,
        output [EDGE_OFF_DWIDTH - VERTEX_MASK_WIDTH - 1 : 0]    mem_edge_ed,
        output                                                  para_valid,

        output                                                  buffer_full_vertex,
        output [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]        dst_id,
        output [EDGE_OFF_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]      edge_off_l,
        output [EDGE_OFF_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]      edge_off_r,
        output [VERTEX_PIPE_NUM - 1 : 0]                        dst_data_valid);

    wire any_front_dst_data_valid;
    wire [VERTEX_PIPE_NUM - 1 : 0] dst_buffer_empty, dst_buffer_full;

    assign any_front_dst_data_valid = front_dst_data_valid[0];
    assign buffer_full_vertex = dst_buffer_full[0];

    edge_info_pre_para_trans P (
        .clk                        (clk),
        .front_rst                  (front_rst),
        .front_edge_off_ed          (front_edge_off_ed),
        .front_edge_info_addr_ed    (front_edge_info_addr_ed),
        .front_mem_edge_ed          (front_mem_edge_ed),
        .front_para_valid           (front_para_valid),

        .rst                        (rst),
        .edge_off_ed                (edge_off_ed),
        .edge_info_addr_ed          (edge_info_addr_ed),
        .mem_edge_ed                (mem_edge_ed),
        .para_valid                 (para_valid));

    generate
        genvar i;
        for (i = 0; i < VERTEX_PIPE_NUM; i = i + 1) begin : M4_BLOCK_1
            edge_info_pre_single V (
                .clk(clk), .rst(front_rst),
                .front_edge_off_l(front_edge_off[(i + 1) * EDGE_OFF_DWIDTH - 1 : i * EDGE_OFF_DWIDTH]),
                .front_edge_off_r(front_edge_off[(i + 2) * EDGE_OFF_DWIDTH - 1 : (i + 1) * EDGE_OFF_DWIDTH]),
                .front_dst_id(front_dst_id[(i + 1) * DST_ID_DWIDTH - 1 : i * DST_ID_DWIDTH]),
                .front_dst_data_valid(front_dst_data_valid[i]),
                .any_front_dst_data_valid(any_front_dst_data_valid), .back_stage_vertex_full(back_stage_vertex_full),

                .buffer_empty(dst_buffer_empty[i]), .buffer_full(dst_buffer_full[i]),
                .dst_id(dst_id[(i + 1) * DST_ID_DWIDTH - 1 : i * DST_ID_DWIDTH]),
                .edge_off_l(edge_off_l[(i + 1) * EDGE_OFF_DWIDTH - 1 : i * EDGE_OFF_DWIDTH]),
                .edge_off_r(edge_off_r[(i + 1) * EDGE_OFF_DWIDTH - 1 : i * EDGE_OFF_DWIDTH]),
                .dst_data_valid(dst_data_valid[i]));
        end
    endgenerate

endmodule

module edge_info_pre_para_trans #(parameter
    EDGE_OFF_DWIDTH     = `EDGE_OFF_DWIDTH,
    MEM_AWIDTH          = `MEM_AWIDTH,
    VERTEX_MASK_WIDTH   = `VERTEX_MASK_WIDTH
    ) (
        input                                                   clk,
        input                                                   front_rst,
        input [MEM_AWIDTH - 1 : 0]                              front_edge_info_addr_ed,
        input [EDGE_OFF_DWIDTH - 1 : 0]                         front_edge_off_ed,
        input [EDGE_OFF_DWIDTH - VERTEX_MASK_WIDTH - 1 : 0]     front_mem_edge_ed,
        input                                                   front_para_valid,

        output reg                                              rst,
        output reg [EDGE_OFF_DWIDTH - 1 : 0]                    edge_off_ed,
        output reg [MEM_AWIDTH - 1 : 0]                         edge_info_addr_ed,
        output reg [EDGE_OFF_DWIDTH - VERTEX_MASK_WIDTH - 1 : 0] mem_edge_ed,
        output reg                                              para_valid);

    always @ (posedge clk) begin
        if (rst) begin
            edge_info_addr_ed   <= 0;
            edge_off_ed         <= 0;
            mem_edge_ed         <= 0;
            para_valid          <= 1'b0;
        end
        else begin
            if (front_para_valid) begin
                edge_info_addr_ed   <= front_edge_info_addr_ed;
                edge_off_ed         <= front_edge_off_ed;
                mem_edge_ed         <= front_mem_edge_ed;
                para_valid          <= 1'b1;
            end
        end
    end

    always @ (posedge clk) begin
        rst <= front_rst;
    end

endmodule

module edge_info_pre_single #(parameter
    DST_ID_DWIDTH = `DST_ID_DWIDTH, EDGE_OFF_DWIDTH = `EDGE_OFF_DWIDTH,
    PIPE_BUFFER_SIZE = `PIPE_BUFFER_SIZE, PIPE_AM_LEVEL = `PIPE_AM_LEVEL, PIPE_BUFFER_PTR_WIDTH = `PIPE_BUFFER_PTR_WIDTH
    ) (
        input                                   clk,
        input                                   rst,
        input [EDGE_OFF_DWIDTH - 1 : 0]         front_edge_off_l,
        input [EDGE_OFF_DWIDTH - 1 : 0]         front_edge_off_r,
        input [DST_ID_DWIDTH - 1 : 0]           front_dst_id,
        input                                   front_dst_data_valid,
        input                                   any_front_dst_data_valid,
        input                                   back_stage_vertex_full,

        output                                  buffer_empty,
        output                                  buffer_full,
        output [DST_ID_DWIDTH - 1 : 0]          dst_id,
        output [EDGE_OFF_DWIDTH - 1 : 0]        edge_off_l,
        output [EDGE_OFF_DWIDTH - 1 : 0]        edge_off_r,
        output                                  dst_data_valid);

    wire                            valid_1;
    wire                            valid_2;
    wire [EDGE_OFF_DWIDTH - 1 : 0]  edge_off_r_p;

    dst_id_fifo idf (
        .clk(clk), .srst(rst),
        .din(front_dst_id), .wr_en(any_front_dst_data_valid), .rd_en(!back_stage_vertex_full),

        .dout(dst_id),
        .empty(buffer_empty), .prog_full(buffer_full));
    
    valid_fifo vf (
        .clk(clk), .srst(rst),
        .din(front_dst_data_valid), .wr_en(any_front_dst_data_valid), .rd_en(!back_stage_vertex_full),

        .dout(valid_1), .valid(valid_2));
    
    edge_off_fifo lf (
        .clk(clk), .srst(rst),
        .din(front_edge_off_l), .wr_en(any_front_dst_data_valid), .rd_en(!back_stage_vertex_full),

        .dout(edge_off_l));

    edge_off_fifo rf (
        .clk(clk), .srst(rst),
        .din(front_edge_off_r), .wr_en(any_front_dst_data_valid), .rd_en(!back_stage_vertex_full),

        .dout(edge_off_r_p));

    assign dst_data_valid = valid_1 && valid_2;
    assign edge_off_r = edge_off_r_p - 1;

endmodule